﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Web;
using EPiCloner.EventHandlers;
using EPiServer;
using EPiServer.Core;
using EPiServer.DataAbstraction;
using EPiServer.DataAccess;
using EPiServer.SpecializedProperties;
using EPiServer.Web;

namespace EPiCloner
{
    public partial class Cloner
    {
        private int PageCloning(PageCloningEventArgs e)
        {
            int pageCount = 0;
            PageCloning(e.SourceReference, e.DestinationReference, e.SourceLanguage, e.TargetLanguage,
                               e.RootPageName, ref pageCount);
            return pageCount;
        }
        private void PageCloning(PageReference sourceRef, PageReference destinationRef, LanguageBranch sourceLanguage, LanguageBranch targetLanguage, string rootPageName, ref int pageCount)
        {
            if (HttpContext.Current != null)
            {
                HttpContext.Current.Server.ScriptTimeout = 300;
            }

            PageData page;
            bool languageExists = false;
            PageDataCollection pdc = DataFactory.Instance.GetLanguageBranches(sourceRef);

            foreach (PageData pageData in pdc)
            {
                if (pageData.LanguageID == sourceLanguage.LanguageID)
                {
                    languageExists = true;
                }
            }

            if (languageExists)
            {
                page = DataFactory.Instance.GetPage(sourceRef, new LanguageSelector(sourceLanguage.LanguageID));
            }
            else
            {
                page = DataFactory.Instance.GetPage(sourceRef);
            }

            var pageName = _isHeadCreated ? string.Empty : rootPageName;
            
            PageData newPage = ClonePage(destinationRef, page, targetLanguage, pageName, _suppressExceptions);
            _isHeadCreated = true;

            DataFactory.Instance.Save(newPage, SaveAction.CheckIn);

            if (CheckOldSelfReferences(page.PageLink, newPage))
            {
                DataFactory.Instance.Save(newPage, SaveAction.Save);
            }

            if (_publish)
            {
                DataFactory.Instance.Save(newPage, SaveAction.Publish);
            }

            if (_updatePageReferences && !PageRefMappings.ContainsKey(page.PageLink))
            {
                PageRefMappings.Add(page.PageLink, newPage.PageLink);
            }

            if (_firstClonedPage == null)
            {
                //never recurse this page.
                _firstClonedPage = newPage.PageLink;
            }

            pageCount++;

            PageDataCollection children = OnGetChildren(new GetChildrenEventArgs(sourceRef, LanguageSelector.AutoDetect(true)));

            if (children != null && children.Count > 0)
            {
                foreach (PageData child in children)
                {
                    //stops an infinate recurstion loop.
                    if (child.PageLink.ID != _firstClonedPage.ID)
                    {
                        PageCloning(child.PageLink, newPage.PageLink, sourceLanguage, targetLanguage, rootPageName, ref pageCount);
                    }
                }
            }
        }

        protected virtual PageDataCollection GetChildrenPages(GetChildrenEventArgs e)
        {
            PageDataCollection children = DataFactory.Instance.GetChildren(e.PageReference, e.Language);

            return children;
        }

        protected virtual PageData ClonePage(PageReference newPageParentRef, PageData page, LanguageBranch lang, string pageName, bool suppressExceptions = false)
        {
            PageData newPage = null;
            try
            {
                newPage = DataFactory.Instance.GetDefaultPageData(newPageParentRef, page.PageTypeID,
                                                                           new LanguageSelector(lang.LanguageID));
                foreach (PropertyData prop in newPage.Property)
                {
                    if (prop != null && (!prop.IsReadOnly) && (prop.Name != "PageLink") &&
                        (prop.Name != "PageParentLink")
                        && (prop.Name != "PageMasterLanguageBranch") && (prop.Name != "PageLanguageBranch") &&
                        (prop.Name != "PageCategory") &&
                        (prop.Name != "PageLanguageID") && (prop.Name != "PageLinkURL") && (prop.Name != "PageGUID") &&
                        (prop.Name != "PageDeleted"))
                    {
                        if (prop.IsRequired && page.Property[prop.Name].Value == null && prop.PropertyValueType.Name.Equals("string", StringComparison.InvariantCultureIgnoreCase))
                        {
                            prop.Value = "Empty Property";
                        }
                        else
                            if (prop.Name == "PageName" && !string.IsNullOrEmpty(pageName) && pageName != "--")
                            {
                                prop.Value = pageName;
                            }
                            else
                            {
                                prop.Value = page.Property[prop.Name].Value;
                            }
                    }
                }
            }
            catch (Exception ex)
            {
                if (suppressExceptions)
                {
                    //Ups...
                }
                else
                {
                    throw;
                }
            }

            return newPage;
        }

        protected virtual bool CheckOldSelfReferences(PageReference originalRef, PageData page)
        {
            bool hasChanges = false;
            foreach (PropertyData prop in page.Property)
            {
                if (!prop.IsReadOnly)
                {
                    if ((prop.Type == PropertyDataType.PageReference) && ((PageReference)prop.Value == originalRef))
                    {
                        prop.Value = page.PageLink;
                        hasChanges = true;
                    }
                }
            }
            return hasChanges;
        }

        void Cloner_PostCloning(object sender, EventArgs e)
        {
            if (PageReference.IsNullOrEmpty(_firstClonedPage) || PageRefMappings == null || PageRefMappings.Count == 0)
            {
                return;
            }

            UpdatePages(_firstClonedPage);
        }

        protected virtual void UpdatePages(PageReference sourceRef)
        {
            var currentPage = DataFactory.Instance.GetPage(sourceRef);
            if (currentPage != null)
            {
                UpdatePageReferences(currentPage);

                PageDataCollection children = DataFactory.Instance.GetChildren(sourceRef, LanguageSelector.AutoDetect(true));

                if (children.Count > 0)
                {
                    foreach (PageData child in children)
                    {
                        UpdatePages(child.PageLink);
                    }
                }
            }

        }

        protected virtual void UpdatePageReferences(PageData currentPage)
        {
            bool isChanged = false;
            PageData newPage = null;
            try
            {
                newPage = currentPage.CreateWritableClone();
                foreach (PropertyData prop in newPage.Property)
                {
                    var pageRef = currentPage.Property[prop.Name].Value as PageReference;
                    if (pageRef != null && !PageReference.IsNullOrEmpty(pageRef) && PageRefMappings.ContainsKey(pageRef))
                    {
                        newPage[prop.Name] = PageRefMappings[pageRef];
                        isChanged = true;
                    }
                    else
                    {
                        var links = currentPage.Property[prop.Name].Value as LinkItemCollection;
                        var newLinks = new LinkItemCollection();
                        if (links != null && links.Count > 0)
                        {
                            foreach (var link in links)
                            {
                                string linkUrl;

                                if (!PermanentLinkMapStore.TryToMapped(link.Href, out linkUrl))
                                {
                                    newLinks.Add(link);
                                    continue;
                                }

                                if (string.IsNullOrEmpty(linkUrl))
                                {
                                    newLinks.Add(link);
                                    continue;
                                }

                                pageRef = PageReference.ParseUrl(linkUrl);

                                if (PageReference.IsNullOrEmpty(pageRef))
                                {
                                    newLinks.Add(link);
                                    continue;
                                }

                                if (PageRefMappings.ContainsKey(pageRef))
                                {
                                    var plm = new PermanentLinkMapper().Find(PageRefMappings[pageRef]).PermanentLinkUrl;

                                    if (plm != null)
                                    {
                                        var li = new LinkItem();
                                        li.Href = plm.OriginalString;
                                        li.Text = link.Text;

                                        newLinks.Add(li);
                                    }
                                }
                            }
                            newPage[prop.Name] = newLinks;
                            isChanged = true;
                        }

                    }
                }
            }
            catch (Exception ex)
            {
                if (_suppressExceptions)
                {
                    //Ups...
                }
                else
                {
                    throw;
                }
            }

            if (isChanged)
            {
                DataFactory.Instance.Save(newPage, SaveAction.CheckIn);

                if (CheckOldSelfReferences(currentPage.PageLink, newPage))
                {
                    DataFactory.Instance.Save(newPage, SaveAction.Save);
                }

                if (_publish)
                {
                    DataFactory.Instance.Save(newPage, SaveAction.Publish);
                }
            }
        }
    }
}
